﻿using System;
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Reflection;
using System.Collections;

/// <summary>
/// ServiceBase 的摘要描述
/// </summary>
public class BaseAjaxService : Page, IHttpHandler
{

    #region 欄位
    // invoke method index
    // key: registerPage.GetType().FullName + invoke method name
    // value: invoke method parameters
    protected static IDictionary<string, IList> invokeMethodDict = new Dictionary<string, IList>();

    // method command index
    // key: registerPage.GetType().FullName + invoke method name
    // value: excute command
    protected static IDictionary<string, string> methodCommandDict = new Dictionary<string, string>();

    // excute command index
    // key: registerPage.GetType().FullName + execute Command
    // value: invoke method name
    protected static IDictionary<string, string> executeCommandDict = new Dictionary<string, string>();
    #endregion

    #region Page Base (.aspx)
    protected void Page_Load(object sender, EventArgs e)
    {
        string command = QueryStringHelper.GetAjaxCommand();

        try
        {
            object obj = processCommand(RegisterMethods(), command);
        }
        catch (Exception ex)
        {
            AjaxHelper.WriteAjaxError(ex.Message, ex);
        }
    }
    #endregion

    #region 泛形 (.ashx)
    public void ProcessRequest(HttpContext context)
    {
        string command = QueryStringHelper.GetAjaxCommand();

        try
        {
            object obj = processCommand(RegisterMethods(), command);
        }
        catch (Exception ex)
        {
            AjaxHelper.WriteAjaxError(ex.Message, ex);
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
    #endregion


    #region 可覆寫的方法
    public virtual Page RegisterMethods()
    {
        // Example code:
        //InsertMethod(this, "executeCommand1", "methodName1"
        //    , new Object[] { this, "executeCommand1" });
        //InsertMethod(this, "executeCommand2", "methodName2"
        //    , new Object[] { this, "executeCommand2" });
        //InsertMethod(this, "executeCommand3", "methodName3"
        //    , new Object[] { this, "executeCommand3" });
        return this;
    }
    #endregion

    #region 公開可用的方法
    public void InsertMethod(Page registerPage, string executeCommand, string methodName)
    { 
        InsertMethod(registerPage, executeCommand, methodName, new Object[] { }); 
    }

    public void InsertMethod(Page registerPage, string executeCommand
        , string methodName, object[] parameters)
    {
        string invokeMethodKey = makeInvokeMethodKey(registerPage, methodName);
        string executeCommandKey = makeExecuteCommandKey(registerPage, executeCommand);

        if (!IsExistMethodByMethodName(registerPage, methodName))
        {
            invokeMethodDict.Add(invokeMethodKey, parameters);
            methodCommandDict.Add(invokeMethodKey, executeCommand);
            executeCommandDict.Add(executeCommandKey, methodName);
        }
    }

    public void ReInsertMethod(Page registerPage, string executeCommand, string methodName)
    { 
        ReInsertMethod(registerPage, executeCommand, methodName, new Object[] { }); 
    }

    public void ReInsertMethod(Page registerPage, string executeCommand
        , string methodName, object[] parameters)
    {
        string invokeMethodKey = makeInvokeMethodKey(registerPage, methodName);
        string executeCommandKey = makeExecuteCommandKey(registerPage, executeCommand);

        RemoveMethodByMethodName(registerPage, methodName);
        InsertMethod(registerPage, executeCommand, methodName, parameters);
    }

    public void RemoveMethodByMethodName(Page registerPage, string methodName)
    {
        string invokeMethodKey = makeInvokeMethodKey(registerPage, methodName);
        if (invokeMethodDict.ContainsKey(invokeMethodKey))
        {
            if (methodCommandDict.ContainsKey(invokeMethodKey))
            {
                string executeCommand = methodCommandDict[invokeMethodKey];
                string executeCommandKey = makeExecuteCommandKey(registerPage
                    , executeCommand);
                if (executeCommandDict.ContainsKey(executeCommandKey))
                {
                    executeCommandDict.Remove(executeCommandKey);
                }
                methodCommandDict.Remove(invokeMethodKey);
            }
            invokeMethodDict.Remove(invokeMethodKey);
        }
    }

    public bool IsExistMethodByMethodName(Page registerPage, string methodName)
    {
        bool isExist = false;
        bool invokeMethodDictIsExist = false;
        bool methodCommandDictIsExist = false;
        bool executeCommandDictIsExist = false;

        string invokeMethodKey = makeInvokeMethodKey(registerPage, methodName);
        if (invokeMethodDict.ContainsKey(invokeMethodKey))
        {
            invokeMethodDictIsExist = true;
            if (methodCommandDict.ContainsKey(invokeMethodKey))
            {
                methodCommandDictIsExist = true;
                string executeCommand = methodCommandDict[invokeMethodKey];
                string executeCommandKey = makeExecuteCommandKey(registerPage
                    , executeCommand);
                if (executeCommandDict.ContainsKey(executeCommandKey))
                {
                    executeCommandDictIsExist = true;
                }
            }
        }

        if (invokeMethodDictIsExist && methodCommandDictIsExist
            && executeCommandDictIsExist)
        { isExist = true; }
        else
        {
            isExist = false;
            RemoveMethodByMethodName(registerPage, methodName);
        }
        return isExist;
    }

    #endregion

    #region 內部使用的方法
    private string makeInvokeMethodKey(Page registerPage, string methodName)
    {
        return registerPage.GetType().FullName + "." + methodName;
    }

    private string makeExecuteCommandKey(Page registerPage, string executeCommand)
    {
        return registerPage.GetType().FullName + "#" + executeCommand;
    }

    private object processCommand(Page page, string command)
    {
        string executeCommandKey = makeExecuteCommandKey(page, command);
        string invokeMethodKey = string.Empty;
        string methodName = string.Empty;
        object[] parameters = null;

        if (!executeCommandDict.ContainsKey(executeCommandKey))
        {
            throw new ArgumentException(
                "msgCommandNotFound",
                new ArgumentException(String.Format(
                    "Page name:{0}, command name:{1}",
                    page.GetType().FullName,
                    command
                ))
            );
        }
        methodName = executeCommandDict[executeCommandKey];
        invokeMethodKey = makeInvokeMethodKey(page, methodName);
        parameters = (object[])invokeMethodDict[invokeMethodKey];

        Type t = page.GetType();
        MethodInfo mi = t.GetMethod(methodName);
        if (mi == null)
        {
            throw new ArgumentException(
                "msgMethodNotFound", 
                new ArgumentException("Page name:" + t.FullName+ ", method name:" + methodName));
        }
        else
        {
            return mi.Invoke(page, parameters);
        }
    }
    #endregion
}
